package self.zhangkang.chart.view.avg

import android.graphics.Canvas
import android.graphics.Paint
import android.graphics.Path
import self.zhangkang.chart.ChartConstants
import self.zhangkang.chart.data.KLineInfo
import self.zhangkang.chart.tools.CanvasTools
import self.zhangkang.chart.tools.DecimalFormatTools
import self.zhangkang.chart.tools.NumberFormatUtils
import self.zhangkang.chart.view.IndexLineInfo
import self.zhangkang.chart.view.IndexNameConfig
import self.zhangkang.chart.view.pool.PathPool
import self.zhangkang.chart.view.pool.RectPool

/**
 * @author : Android-张康
 * created on: 2023/5/9 18:19
 * description: 分时图
 */
internal class AvgChart(
    private val pathPool: PathPool,
    private val rectPool: RectPool,
    private val avgViewColor: AvgViewColor,
    private val avgViewDimen: AvgViewDimen,
    private val indexNameConfig: IndexNameConfig
) : AvgIndexInfo {

    companion object {

        private const val LINE_TOP_BOTTOM_SPACE = 0.001F

    }

    private val avgPriceLineInfo = object : IndexLineInfo {
        override fun getName(): String {
            return "均价"
        }

        override fun getLineColor(): Int {
            return avgViewColor.avgPriceTextColor
        }

        override fun getIndexValue(kLineInfo: KLineInfo): Float {
            return kLineInfo.avgPrice
        }
    }

    /**
     * 记录开盘价
     */
    private var mOpenPrice = 1F

    /**
     * 记录最高价
     */
    private var mMaxValue: Float = mOpenPrice * (1 + LINE_TOP_BOTTOM_SPACE)

    /**
     * 记录最低价
     */
    private var mMinValue: Float = mOpenPrice * (1 - LINE_TOP_BOTTOM_SPACE)

    private var priceItem: Float = 0.0F

    /**
     * 分时的路径
     */
    private var mAvgPath = Path()

    private var mAvgPriceEnabled = true

    private val indexLineInfoList = mutableListOf<IndexLineInfo>(
        avgPriceLineInfo
    )

    private val indexLineInfoPath = mutableMapOf<IndexLineInfo, Path>()

    /**
     * 计算空值
     */
    override fun calculationEmpty() {
        mOpenPrice = 1F
        mMaxValue = mOpenPrice * (1 + LINE_TOP_BOTTOM_SPACE)
        mMinValue = mOpenPrice * (1 - LINE_TOP_BOTTOM_SPACE)
    }

    /**
     * 获取指标类型
     */
    override fun getIndexInfo(): AvgChartIndex {
        return AvgChartIndex.AvgChart
    }

    /**
     * 获取占的比例
     */
    override fun getProportion(): Int {
        return 3
    }

    /**
     * 计算最大值前
     *
     * @param kLineInfo 第一个值
     */
    override fun calculationMaxAndMinBefore(kLineInfo: KLineInfo) {
        mOpenPrice = if (kLineInfo.preCLosePrice.isFinite()) {
            kLineInfo.preCLosePrice
        } else {
            kLineInfo.openPrice
        }
        mMaxValue = kLineInfo.closePrice
        mMinValue = kLineInfo.closePrice
    }

    /**
     * 计算最大值
     *
     * @param kLineInfo 当前值
     */
    override fun calculationMaxAndMin(kLineInfo: KLineInfo) {
        if (kLineInfo.closePrice > mMaxValue) {
            mMaxValue = kLineInfo.closePrice
        }
        if (kLineInfo.closePrice < mMinValue) {
            mMinValue = kLineInfo.closePrice
        }
        if (mAvgPriceEnabled) {
            indexLineInfoList.forEach {
                val value = it.getIndexValue(kLineInfo)
                if (value > mMaxValue) {
                    mMaxValue = value
                }
                if (value < mMinValue) {
                    mMinValue = value
                }
            }
        }
    }

    /**
     * 计算最大值后
     */
    override fun calculationMaxAndMinAfter() {
        mMaxValue += mOpenPrice * LINE_TOP_BOTTOM_SPACE
        mMinValue -= mOpenPrice * LINE_TOP_BOTTOM_SPACE
        if (mMinValue < 0) {
            mMinValue = 0F
            mMaxValue = mOpenPrice + (mOpenPrice - mMinValue)
        }
        mMaxValue = mMaxValue.coerceAtLeast(mOpenPrice * (1 + LINE_TOP_BOTTOM_SPACE))
        mMinValue = mMinValue.coerceAtMost(mOpenPrice * (1 - LINE_TOP_BOTTOM_SPACE))
        val maxDiff = mMaxValue - mOpenPrice
        val minDiff = mOpenPrice - mMinValue
        if (maxDiff >= minDiff) {
            mMinValue = mOpenPrice - maxDiff
        } else {
            mMaxValue = mOpenPrice + minDiff
        }
    }

    /**
     * 绘制背景
     *
     * @param canvas 画布
     * @param startX 开始X
     * @param startY 开始Y
     * @param stopX 结束X
     * @param stopY 结束Y
     * @param linePaint 画布
     * @param textPaint 画布
     */
    override fun drawBackground(
        canvas: Canvas,
        startX: Float,
        startY: Float,
        stopX: Float,
        stopY: Float,
        linePaint: Paint,
        textPaint: Paint
    ) {
        linePaint.strokeWidth = avgViewDimen.borderLineStrokeWidth
        linePaint.color = avgViewColor.borderLineColor
        linePaint.style = Paint.Style.STROKE
        //绘制边框的线
        val backgroundPath = pathPool.acquire()
        val borderStopY = stopY - avgViewDimen.dateSpace
        val halfLine = avgViewDimen.borderLineStrokeWidth / 2
        canvas.drawPath(backgroundPath.apply {
            reset()
            moveTo(startX + halfLine, startY)
            lineTo(stopX - halfLine, startY)
            lineTo(stopX - halfLine, borderStopY)
            lineTo(startX + halfLine, borderStopY)
            close()
        }, linePaint)
        val itemWidth = (stopX - startX) / 4
        linePaint.color = avgViewColor.dashLineColor
        linePaint.pathEffect = avgViewDimen.dashPathEffect
        //绘制竖向分割线
        repeat(5) {
            if (it == 1 || it == 2 || it == 3) {
                val itemStopX = startX + itemWidth * it
                val linePath = pathPool.acquire()
                linePath.moveTo(itemStopX, startY)
                linePath.lineTo(itemStopX, borderStopY)
                canvas.drawPath(linePath, linePaint)
                pathPool.release(linePath)
            }
        }
        linePaint.pathEffect = null
        //绘制横线同时绘制纵坐标
        //每个分段对应的高
        val itemHeight = (borderStopY - startY) / 4
        val itemPrice = (mMaxValue - mMinValue) / 2
        val textStartX = startX + avgViewDimen.textPaddingLine
        textPaint.apply {
            color = avgViewColor.ordinateTextColor
            textSize = avgViewDimen.ordinateTextSize
        }
        repeat(5) {
            val itemStopY = startY + itemHeight * it
            when (it) {
                0 -> {
                    val leftText = DecimalFormatTools.retainTwoDecimalPlaces(mMaxValue)
                    val textBoundsRect = rectPool.acquire()
                    textPaint.getTextBounds(leftText, 0, leftText.length, textBoundsRect)
                    textPaint.color = avgViewColor.redColor
                    val textY =
                        itemStopY + textBoundsRect.height() + avgViewDimen.textPaddingLine
                    canvas.drawText(
                        leftText,
                        textStartX,
                        textY,
                        textPaint
                    )

                    val rightNumber = (mMaxValue - mOpenPrice) / mOpenPrice
                    val rightText = if (rightNumber.isFinite()) {
                        NumberFormatUtils.numberToPercentage(rightNumber)
                    } else {
                        "0.00%"
                    }
                    textPaint.getTextBounds(rightText, 0, rightText.length, textBoundsRect)
                    canvas.drawText(
                        rightText,
                        stopX - textBoundsRect.width() - avgViewDimen.textPaddingLine,
                        textY,
                        textPaint
                    )
                    rectPool.release(textBoundsRect)
                }

                2 -> {
                    val linePath = pathPool.acquire()
                    linePath.moveTo(startX, itemStopY)
                    linePath.lineTo(stopX, itemStopY)
                    linePaint.pathEffect = avgViewDimen.dashPathEffect
                    linePaint.color = avgViewColor.highlightDashLineColor
                    linePaint.strokeWidth = avgViewDimen.borderLineStrokeWidth
                    canvas.drawPath(linePath, linePaint)
                    linePaint.pathEffect = null
                    pathPool.release(linePath)
                    val leftText = DecimalFormatTools.retainTwoDecimalPlaces(mOpenPrice)
                    val textBoundsRect = rectPool.acquire()
                    textPaint.getTextBounds(leftText, 0, leftText.length, textBoundsRect)
                    textPaint.color = avgViewColor.grayColor
                    val textY = itemStopY + textBoundsRect.height() / 2
                    canvas.drawText(
                        leftText,
                        textStartX,
                        textY,
                        textPaint
                    )
                    val rightText = "0.00%"
                    textPaint.getTextBounds(rightText, 0, rightText.length, textBoundsRect)
                    canvas.drawText(
                        rightText,
                        stopX - textBoundsRect.width() - avgViewDimen.textPaddingLine,
                        textY,
                        textPaint
                    )
                    rectPool.release(textBoundsRect)
                }

                4 -> {
                    val leftText = DecimalFormatTools.retainTwoDecimalPlaces(mMinValue)
                    textPaint.color = avgViewColor.greenColor
                    val textY = itemStopY - avgViewDimen.textPaddingLine

                    canvas.drawText(
                        leftText,
                        textStartX,
                        textY,
                        textPaint
                    )
                    val rightNumber = (mMinValue - mOpenPrice) / mOpenPrice
                    val rightText = if (rightNumber.isFinite()) {
                        NumberFormatUtils.numberToPercentage(rightNumber)
                    } else {
                        "0.00%"
                    }
                    val textBoundsRect = rectPool.acquire()
                    textPaint.getTextBounds(rightText, 0, rightText.length, textBoundsRect)
                    canvas.drawText(
                        rightText,
                        stopX - textBoundsRect.width() - avgViewDimen.textPaddingLine,
                        textY,
                        textPaint
                    )
                    rectPool.release(textBoundsRect)
                }

                else -> {
                    val linePath = pathPool.acquire()
                    linePath.moveTo(startX, itemStopY)
                    linePath.lineTo(stopX, itemStopY)
                    linePaint.color = avgViewColor.dashLineColor
                    linePaint.pathEffect = avgViewDimen.dashPathEffect
                    canvas.drawPath(linePath, linePaint)
                    linePaint.pathEffect = null
                    pathPool.release(linePath)
                }
            }
        }
        pathPool.release(backgroundPath)
        drawDate(canvas, startX, borderStopY, stopX, stopY, textPaint)
    }

    /**
     * 绘制当前值
     *
     * @param canvas 画布
     * @param startX 开始的 X
     * @param startY 开的的 Y
     * @param stopX 结束的 X
     * @param stopY 结束的 Y
     * @param linePaint 线的画笔
     * @param textPaint 文字的画笔
     * @param marginTop 距离顶部的距离
     * @param span
     * @param indexValueHorizontalSpace 指标值之间的横向距离
     * @param indexValueVerticalSpace 指标值之间的纵向距离
     * @param kLineInfo 当前的值
     * @param kLineIsLine K线是否为一条线
     */
    override fun drawCurrentIndexInfo(
        canvas: Canvas,
        startX: Float,
        startY: Float,
        stopX: Float,
        stopY: Float,
        linePaint: Paint,
        textPaint: Paint,
        marginTop: Float,
        span: Int,
        indexValueHorizontalSpace: Float,
        indexValueVerticalSpace: Float,
        kLineInfo: KLineInfo,
        kLineIsLine: Boolean
    ) {

        val closePrice = kLineInfo.closePrice
        val diffPrice = closePrice - mOpenPrice
        val drawText = buildString {
            append("最新价：")
            append(NumberFormatUtils.priceNumberFormat(closePrice))
            append(ChartConstants.EMPTY_STR)
            if (closePrice > mOpenPrice) {
                append("+")
            }
            append(NumberFormatUtils.priceNumberFormat(diffPrice))
            append(ChartConstants.EMPTY_STR)
            val percentage = diffPrice / mOpenPrice
            if (percentage.isFinite()) {
                append(NumberFormatUtils.numberToPercentage(percentage))
            } else {
                append("0.00%")
            }
        }
        textPaint.textSize = avgViewDimen.currentValueTextSize
        textPaint.color = if (closePrice > mOpenPrice) {
            avgViewColor.redColor
        } else if (closePrice < mOpenPrice) {
            avgViewColor.greenColor
        } else {
            avgViewColor.grayColor
        }
        val avgTexStart = startX + indexNameConfig.marginStart
        val avgTextWidth = CanvasTools.drawTextInVerticalCenter(
            canvas,
            avgTexStart,
            startY,
            stopY,
            drawText,
            textPaint,
            rectPool = rectPool
        )
        if (!mAvgPriceEnabled) {
            return
        }
        var indicatorStart = avgTexStart + avgTextWidth + indexValueHorizontalSpace
        var drawTextInVerticalCenter: Int
        indexLineInfoList.forEach { maInfo ->
            val indexValue = maInfo.getIndexValue(kLineInfo)
            val indexText = if (indexValue.isFinite()) {
                NumberFormatUtils.priceNumberFormat(indexValue)
            } else {
                "--"
            }
            val currentString = "${maInfo.getName()}：$indexText"
            textPaint.color = maInfo.getLineColor()
            drawTextInVerticalCenter = CanvasTools.drawTextInVerticalCenter(
                canvas,
                indicatorStart,
                startY,
                stopY,
                currentString,
                textPaint,
                rectPool = rectPool
            )
            indicatorStart += drawTextInVerticalCenter + indexValueHorizontalSpace
            val indexPath = pathPool.acquire()
            indexLineInfoPath[maInfo] = indexPath
        }
    }

    /**
     * 绘制当前值内容之前
     *
     * @param canvas 画布
     * @param startX 开始的 X
     * @param startY 开的的 Y
     * @param stopX 结束的 X
     * @param stopY 结束的 Y
     * @param paint 画笔
     * @param candlestickWidth 蜡烛的宽度
     * @param candlestickWidthSpace 蜡烛之间的距离
     * @param kLineInfo 当前的值
     * @param kLineIsLine K线是否为一条线
     */
    override fun beforeDrawItemContent(
        canvas: Canvas,
        startX: Float,
        startY: Float,
        stopX: Float,
        stopY: Float,
        paint: Paint,
        candlestickWidth: Float,
        candlestickWidthSpace: Float,
        kLineInfo: KLineInfo,
        kLineIsLine: Boolean
    ) {
        val valueRange = mMaxValue - mMinValue
        val borderStopY = stopY - avgViewDimen.dateSpace
        val drawHeight = borderStopY - startY
        priceItem = drawHeight / valueRange
        mAvgPath = pathPool.acquire()
    }

    /**
     * 绘制当前值内容
     *
     * @param canvas 画布
     * @param startX 开始的 X
     * @param startY 开的的 Y
     * @param stopX 结束的 X
     * @param stopY 结束的 Y
     * @param textPaint 文字的画笔
     * @param linePaint 线的画笔
     * @param kLineInfo 当前的值
     * @param isFirst 是否为第一次
     * @param yangLineIsFill 阳线是否实心
     * @param totalTranslateX X轴总的位移
     * @param candlestickWidth 蜡烛的宽度
     * @param halfCandlestickWidth 蜡烛的一半宽度
     * @param kLineIsLine K线是否为一条线
     */
    override fun drawItemContent(
        canvas: Canvas,
        startX: Float,
        startY: Float,
        stopX: Float,
        stopY: Float,
        textPaint: Paint,
        linePaint: Paint,
        kLineInfo: KLineInfo,
        isFirst: Boolean,
        yangLineIsFill: Boolean,
        totalTranslateX: Float,
        candlestickWidth: Float,
        halfCandlestickWidth: Float,
        kLineIsLine: Boolean
    ) {
        val borderStopY = stopY - avgViewDimen.dateSpace
        //Path 的横坐标
        val pathX = startX + halfCandlestickWidth + totalTranslateX
        val avgValue = kLineInfo.closePrice
        val indexY = priceItem * (mMaxValue - avgValue) + startY
        val avgPath = mAvgPath
        if (isFirst) {
            if (indexY <= borderStopY) {
                avgPath.moveTo(pathX, indexY)
            } else {
                avgPath.moveTo(pathX, borderStopY)
            }
        } else {
            if (indexY <= borderStopY) {
                avgPath.lineTo(pathX, indexY)
            }
        }
        if (mAvgPriceEnabled) {
            indexLineInfoPath.forEach {
                val indexValue = it.key.getIndexValue(kLineInfo)
                val maY = priceItem * (mMaxValue - indexValue) + startY
                val path = it.value
                if (isFirst) {
                    if (maY <= stopY) {
                        path.moveTo(pathX, maY)
                    } else {
                        path.moveTo(pathX, stopY)
                    }
                } else {
                    if (maY <= stopY) {
                        path.lineTo(pathX, maY)
                    }
                }
            }
        }
    }

    /**
     * 绘制完整个当前内容之后
     *
     * @param canvas 画布
     * @param paint 画笔
     */
    override fun afterDrawItemContent(canvas: Canvas, paint: Paint) {
        val avgLineColor = avgViewColor.avgLineColor
        val avgPath = mAvgPath
        paint.color = avgLineColor
        paint.strokeWidth = avgViewDimen.avgLineStrokeWidth
        canvas.drawPath(avgPath, paint)
        pathPool.release(avgPath)
        if (mAvgPriceEnabled) {
            indexLineInfoPath.forEach {
                val maColor = it.key.getLineColor()
                val maPath = it.value
                paint.color = maColor
                canvas.drawPath(maPath, paint)
                pathPool.release(maPath)
            }
        }
        indexLineInfoPath.clear()
    }

    /**
     * 获取当前左边的值
     *
     * @param percentage 比例
     *
     * @return 返回当前左边坐标的值
     */
    override fun getCurrentLeftValue(percentage: Float): String {
        val currentValue = getCurrentValue(percentage)
        return NumberFormatUtils.priceNumberFormat(currentValue)
    }

    /**
     * 获取当前右边的值
     *
     * @param percentage 比例
     *
     * @return 返回当前右边坐标的值
     */
    override fun getCurrentRightValue(percentage: Float): String {
        val currentValue = getCurrentValue(percentage)
        return NumberFormatUtils.numberToPercentage((currentValue - mOpenPrice) / mOpenPrice)
    }

    /**
     * 设置均线是否启用
     *
     * @param enabled true 启用 false 不启用
     */
    fun setEnabledAvgPrice(enabled: Boolean) {
        mAvgPriceEnabled = enabled
    }

    /**
     * 均线是否启用
     *
     */
    fun isAvgPriceEnabled() = mAvgPriceEnabled

    /**
     * 绘制日期
     *
     * @param canvas 画布
     * @param startX 开始绘制的 X 坐标
     * @param startY 开始绘制的 Y 坐标
     * @param stopX 结束绘制的 X 坐标
     * @param stopY 结束绘制的 Y 坐标
     * @param textPaint 文字的画笔
     */
    private fun drawDate(
        canvas: Canvas,
        startX: Float,
        startY: Float,
        stopX: Float,
        stopY: Float,
        textPaint: Paint
    ) {
        val startDate = "9:30"
        textPaint.textSize = avgViewDimen.dateTextSize
        textPaint.color = avgViewColor.dateTextColor
        val textBoundsRect = rectPool.acquire()
        textPaint.getTextBounds(startDate, 0, startDate.length, textBoundsRect)
        val startDateX = startX + avgViewDimen.textPaddingLine
        val startDateY = startY + textBoundsRect.height() + avgViewDimen.datePaddingTop
        canvas.drawText(startDate, startDateX, startDateY, textPaint)

        val midDate = "11:30/13:00"
        textPaint.getTextBounds(midDate, 0, midDate.length, textBoundsRect)
        val midDateX = startX + (stopX - startX - textBoundsRect.width()) / 2
        val midDateY = startY + textBoundsRect.height() + avgViewDimen.datePaddingTop
        canvas.drawText(midDate, midDateX, midDateY, textPaint)

        val endDate = "15:00"
        textPaint.getTextBounds(endDate, 0, endDate.length, textBoundsRect)
        val endDateX = stopX - textBoundsRect.width() - avgViewDimen.textPaddingLine
        val endDateY = startY + textBoundsRect.height() + avgViewDimen.datePaddingTop
        canvas.drawText(endDate, endDateX, endDateY, textPaint)
        rectPool.release(textBoundsRect)
    }

    /**
     * 获取当前值
     *
     * @param percentage 比例
     */
    private fun getCurrentValue(percentage: Float): Float {
        if (percentage >= 1.0F) {
            return mMaxValue
        }
        if (percentage <= 0.0F) {
            return mMinValue
        }
        return mMinValue + (mMaxValue - mMinValue) * percentage
    }
}